/**
 * @file tiny_schedule.c
 * @brief 负责调度相关管理.
 * @author 张恒宣 
 * @LastEditTime 2024/9/22
 * 
 * MIT License
 *
 * Copyright (c) 2025 张恒宣 luckkeymail@163.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*********************
 *      头文件
 *********************/

#include "tiny_thread.h"
#include "tiny_rtos_context.h"
#include "tiny_malloc.h"
#include "tiny_timer.h"

/*********************
*    外部引用变量
*********************/

extern tiny_list_t thread_exit_list;
extern tiny_list_t thread_suspend_list;
extern tiny_list_t thread_ready_list[TINY_THREAD_MAX_PRIORITY];
extern tiny_thread_t *cur_running_thread;

extern unsigned int from_sp_addr;
extern unsigned int to_sp_addr;
extern unsigned int context_enable_flag;


/*********************
*  辅助切换环境变量 
*********************/

unsigned int from_sp_addr;
unsigned int to_sp_addr;
unsigned int context_enable_flag;

/*********************
*     私有变量 
*********************/

static tiny_int8_t is_open_schedule = 0;
static tiny_uint64_t tick_count = 0;

#if IS_OPEN_GET_CPU == PROFILE_OPEN
extern tiny_uint32_t all_runtime;
#endif

/*********************
*     私有函数 
*********************/

/// @brief 寻找最高优先级就绪线程
/// @param  无需
/// @return 最高优先级就绪线程的优先级
int tiny_find_high_ready_priority(void)
{
	for (int i = TINY_THREAD_MAX_PRIORITY - 1; i >= 0; --i)
	{
		if (thread_ready_list[i].len > 0)
		{
			return i;
		}
	}
	return 0;
}

/// @brief 空闲线程.
/// @param param 
NORETUTN static void __attribute__((optnone, noinline)) tiny_thread_idle(void *param)
{
	UNUSED_PARAMETER(param);
	while (1)
	{
		/* 负责清理退出线程. */
		if (thread_exit_list.len > 0)
		{
			int flag = disable_interrupt();
			tiny_list_node_t *node = thread_exit_list.head;
			do
			{
				tiny_thread_t *thread = TINY_THREAD_ENTRY(node);
				tiny_remove_node_from_list(&thread->list_all_thread_link);
				
				if (thread->exit_hook) {
					thread->exit_hook(thread->exit_hook_param);
				}
				tiny_free(thread->stack);
				tiny_free(thread);
				node = node->next;
			} while (thread_exit_list.head != node);

			thread_exit_list.head = tiny_null;
			thread_exit_list.len = 0;
			enable_interrupt(flag);
		}
		__enter_sleep_mode();
	}
}

#if IS_OPEN_GET_CPU == PROFILE_OPEN

static void update_thread_cpu_usage_callback(tiny_thread_t* thread)
{
	thread->cpu_usage  =  (float)tiny_thread_get_thread_run_time(thread) / (float)tiny_thread_get_all_thread_run_time() * 100.0f;
	tiny_thread_clear_thread_run_time(thread);
}

/// @brief 统计CPU占用率定时器.
/// @param param 
static void tiny_timer_cpu(void *param)
{
	UNUSED_PARAMETER(param);
	if (0 == tiny_thread_get_all_thread_run_time())
		return;
	tiny_list_thread(update_thread_cpu_usage_callback);
	tiny_thread_clear_all_thread_run_time();
}

#endif

/*********************
*     公共函数 
*********************/

/// @brief 打开调度器
/// @param  无需
void tiny_open_schedule(void)
{
	is_open_schedule = 1;
}

/// @brief 关闭调度器
/// @param  无需
void tiny_close_schedule(void)
{
	is_open_schedule = 0;
}

/// @brief 获取调度器状态
/// @param  无需
tiny_int8_t tiny_get_schedule(void)
{
	return is_open_schedule;
}

/// @brief 调度器, 负责切换线程
/// @param  无需
void schedule(void) 
{
	static int s_isfirst = 1; // 防止对 if-else 优化用
	if (!is_open_schedule || context_enable_flag)
	{
		return;
	}

	int flag = disable_interrupt();
	/* 找到最高优先级线程 */
	int max_high_priority = tiny_find_high_ready_priority();
	tiny_thread_t *thread = TINY_THREAD_ENTRY(thread_ready_list[max_high_priority].head);
	
	
#if IS_OPEN_GET_CPU == PROFILE_OPEN
	if (cur_running_thread)
	{
		tiny_uint64_t detal = (tiny_get_tick_count() - cur_running_thread->schedule_last_tick) + 1;
		cur_running_thread->run_time += detal;
		all_runtime += detal;
	}
#endif

	if (thread->statu == THREAD_RUNNING || thread == cur_running_thread)
	{
		enable_interrupt(flag);
		return;
	}
	
	if (cur_running_thread->statu == THREAD_RUNNING)
	{
		cur_running_thread->statu = THREAD_READY;
	}

	thread->statu = THREAD_RUNNING;
	tiny_thread_t *old_running_thread = cur_running_thread;
	
	

	cur_running_thread = thread;
	
#if IS_OPEN_GET_CPU == PROFILE_OPEN
	cur_running_thread->schedule_last_tick = tiny_get_tick_count();
#endif
	
	if (!s_isfirst)
	{
		/* 执行调度 */
		switch_contexts((unsigned int)&old_running_thread->sp, (unsigned int)&cur_running_thread->sp);
	}
	else
	{
		s_isfirst = 0;
		/* 执行第一次调度. */
		switch_contexts_first((unsigned int)&cur_running_thread->sp);
	}
	enable_interrupt(flag);
}

/// @brief 获取系统启动以来的时间tick
/// @param  无需
tiny_uint64_t tiny_get_tick_count(void)
{
	return tick_count;
}

/// @brief 系统定时器, 每一个tick调用一次, 需要在系统定时器中断中调用
/// @param  无需
void tiny_kernel_system_tick(void)
{
	if (is_open_schedule && cur_running_thread)
	{
		tiny_uint8_t is_schedule = 0;
		tick_count++;	
		
		if (0 == cur_running_thread->remain_tick)
		{
			cur_running_thread->remain_tick = cur_running_thread->init_tick;
			tiny_list_head_move_to_next(&thread_ready_list[cur_running_thread->cur_priority]);
			is_schedule = 1;
		}
		else
		{
			cur_running_thread->remain_tick--;
		}	
		
		if (TIMER_OWNER_THREAD == tiny_check_timers())
		{
			is_schedule = 1;
		}
			
		if (is_schedule)
		{
			schedule();
		}
	}
}

/// @brief 启动内核
/// @param  无需
tiny_ret_status_t tiny_kernel_startup(void)
{
	/* 创建空闲线程 */
	tiny_thread_hander idle_thread = tiny_thread_create("idle_thread", 512, 0, tiny_thread_idle, tiny_null);
	if (idle_thread)
	{
		tiny_thread_startup(idle_thread);
	}
	else
	{
		/* 堆栈不足. */
		return tiny_false;
	}

	/* 初始化定时器线程 . */
	tiny_timer_init();
	
#if IS_OPEN_GET_CPU == PROFILE_OPEN

	/* 创建统计cpu占用率定时器 */
	tiny_timer_t *timer_cpu = tiny_timer_create(TIMER_PERIODIC, CONVERT_MS_TO_TICK(GET_CPU_TIME), tiny_timer_cpu, tiny_null);
	if (timer_cpu)
	{
		tiny_timer_start(timer_cpu);
	}
	else
	{
		/* 堆栈不足. */
		return tiny_false;
	}
	
#endif

	cur_running_thread = tiny_null;

	/* 启动调度器. */
	tiny_open_schedule();
	
	/* 执行调度. */
	schedule();

	/* 永远不会到这里. */
	
	UNUSED_VARIABLE(from_sp_addr);
	UNUSED_VARIABLE(to_sp_addr);
	UNUSED_VARIABLE(context_enable_flag);

	return tiny_false;
}

